package com.onpositive.pagerduty.grabber;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.onpositive.html.model.HyperLink;
import com.onpositive.html.model.HyperLinkEntry;
import com.onpositive.html.model.ITableEntry;
import com.onpositive.html.model.StringEntry;
import com.onpositive.html.model.Table;
import com.onpositive.html.utils.TableGrabber;
import com.onpositive.rest.model.HttpType;
import com.onpositive.rest.model.Method;
import com.onpositive.rest.model.Param;
import com.onpositive.rest.model.Resource;
import com.onpositive.rest.model.Response;

public class PagerDutyGrabber {

	
	public Resource grabResource(String ref) throws IOException{
		
		Document doc = Jsoup.connect(ref).get();
		String baseUri = doc.baseUri();
		
		Resource res = new Resource();
		Elements breadcrubElements = doc.select("breadcrumb");
		if(!breadcrubElements.isEmpty()){
			Element first = breadcrubElements.first();
			int count = first.select("li").size();
			count-=2;
			res.setLevel(count);
		}
		
		List<Table> tables = TableGrabber.grabTables(doc);
		
		for(Table t : tables){
			if(t==null)
				continue;
			
			String caption = t.getCaption();
			if(caption==null)
				continue;
			
			res.setDescription(caption);
			break;
		}
		
		for(Table t : tables){
			if(t==null)
				continue;
			
			if(t.getColumns().isEmpty())
				continue;
			
			ArrayList<Method> methods = extractMethods(t,baseUri);
			for(Method m : methods){				
				registerMethod(res, m, m.getUri());
			}
		}
		
		return res;
	}

	private void registerMethod(Resource resource, Method m, String uri) {
		if(uri.startsWith("/"))
			uri=uri.substring(1);
		
		if(uri.endsWith("/"))
			uri = uri.substring(0, uri.length()-1);
		
		int ind = uri.indexOf("/");
		if(ind<0){
			resource.getMethods().put(m.getSignature(), m);
			resource.setUrl("/"+uri);
			resource.getMethods().put(m.getSignature(), m);
		}
		else{
			String uri1 = uri.substring(0, ind);
			resource.setUrl("/"+uri1);
			int ind2 = uri.indexOf("/", ind+1);
			if(ind2<0)
				ind2=uri.length();
			
			String chName = "/"+uri.substring(ind, ind2);
			Resource chRes = resource.getChildResources().get(chName);
			if(chRes==null){
				chRes=new Resource();
				resource.getChildResources().put(chName, chRes);
				chRes.setLevel(resource.getLevel()+1);
			}
			registerMethod(chRes, m, uri.substring(ind+1));
		}
	}

	private ArrayList<Method> extractMethods(Table t, String baseUri) {
		
		ArrayList<ArrayList<ITableEntry>> columns = t.getColumns();
		ArrayList<Method> methods = new ArrayList<Method>();
		
		int length = Integer.MAX_VALUE;
		for( ArrayList<ITableEntry> column : columns){
			length=Math.min(length, column.size());
		}
		for(int i = 0 ; i < length; i++){
			
			String path=null;
			HttpType httpType=null;
			String description=null;
			String link = null;
			
			ITableEntry entry0 = columns.get(0).get(i);
			ITableEntry entry1 = columns.get(1).get(i);
			if(entry0 instanceof HyperLinkEntry){
				HyperLinkEntry hle = (HyperLinkEntry) entry0;
				HyperLink hl = hle.getObject();
				link = hl.getLink();
				String caption = hl.getCaption();

				for( HttpType ht : HttpType.values() ){
					if( !caption.startsWith(ht.name()) )
						continue;
					
					path = caption.substring(ht.name().length()).trim();
					httpType = ht;
					break;
				}
			}
			if(path==null)
				continue;
			
			if(entry1 instanceof StringEntry){
				StringEntry se = (StringEntry) entry1;
				description = se.getObject();
			}
			
			Method method = extractMethod(path,httpType,link,description,baseUri);
			if(method!=null)
				methods.add(method);
		}
		return methods;
	}
	
	private Method extractMethod(String path, HttpType httpType, String link, String description, String baseUri)
	{
		try {
			URL url = new URL(baseUri);
			URL methodUrl = new URL(url, link);
			String gLink = methodUrl.toExternalForm();
			Document doc = Jsoup.connect(gLink).get();
			Method method = grabMethod(doc);
			method.setDescription(description);
			method.setHttpType(httpType);
			method.setUri(path);
			return method;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@SuppressWarnings("unused")
	public Method grabMethod(Document doc){
		
		Method method = new Method();
		List<Table> tables = TableGrabber.grabTables(doc);
		extractParams(method,tables);
		extractExamples(method,doc);
		return method;
	}

	private void extractExamples(Method method, Document doc) {
		
		Elements preElements = doc.select("pre");
		for(Element pre : preElements){
			String classAttr = pre.attr("class");
			String text = pre.text();
			if("prettyprint curl".equals(classAttr)){
				method.getBody().addExample(text);
			}
			else if("prettyprint linenums".equals(classAttr)){
				int status = findStatus(pre) ;
				Response resp = method.getResponses().get(status);
				if(resp==null){
					resp = new Response();
					resp.setCode(status);
					method.getResponses().put(status, resp);
				}
				resp.getBody().addExample(text);
			}
		}
		
		
	}

	private int findStatus(Element pre) {
		int val = -1;
		try{
			Element parent = pre.parent().parent();
			Elements children = parent.select("h4");
			int si = pre.parent().siblingIndex();
l0:			for(int i = children.size() ; i > 0 ; i--){
				Element element = children.get(i-1);
				if( element.siblingIndex()>si)
					continue;
				
				if(!element.text().toLowerCase().contains("response"))
					continue;
				
				Elements spanElements = element.select("span");
				for(Element span : spanElements){
					String classAttr = span.attr("class");
					if(!"response-status".equals(classAttr))
						continue;
					
					String text = element.text();			
					String dig = text.replaceAll("[^\\d]", "");
					val = Integer.parseInt(dig);
					break l0;
				}
				
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
		return val;
	}

	private void extractParams(Method method, List<Table> tables) {
		LinkedHashMap<String, Param> mainParams = method.getParams();
		for(Table t : tables){
			String tableTitle = t.getTitle();
			if("Parameters".equals(tableTitle)){
				extractParams(t, mainParams);
			}
			else{
				LinkedHashMap<String, LinkedHashMap<String, Param>> otherParams = method.getOtherParams();
				LinkedHashMap<String, Param> map = otherParams.get(tableTitle);
				if(map==null){
					map=new LinkedHashMap<String, Param>();
					otherParams.put(tableTitle, map);
				}
				extractParams(t, map);
			}			
		}
	}

	private void extractParams(Table t, LinkedHashMap<String, Param> paramsMap)
	{
		ArrayList<ArrayList<ITableEntry>> columns = t.getColumns();
		if(columns.isEmpty())
			return;
		int length = Integer.MAX_VALUE;
		for(ArrayList<ITableEntry> column : columns){
			length = Math.min(length, column.size());
		}
		int nameIndex=0;
		int typeIndex=1<columns.size()? 1 : -1;
		int reqIndex=2<columns.size()? 2 : -1;
		int descIndex=3<columns.size()? 3 : -1;
		if(!t.getHeaders().isEmpty()){
			ArrayList<String> hd = t.getHeaders().get(0);
			for(int i = 0 ; i < hd.size() ; i++){
				String str = hd.get(i);
				if("name".equals(str.toLowerCase().trim())){
					nameIndex=i;
				}
				else if("type".equals(str.toLowerCase().trim())){
					typeIndex=i;
				}
				else if("required".equals(str.toLowerCase().trim())){
					reqIndex=i;
				}
				else if("description".equals(str.toLowerCase().trim())){
					descIndex=i;
				}
			}
		}
		
		for(int i = 0 ; i < length ; i++){
			ITableEntry nameEntry = columns.get(nameIndex).get(i);
			ITableEntry typeEntry = typeIndex >=0 ? columns.get(typeIndex).get(i) : null;
			ITableEntry requiredEntry = reqIndex >=0 ? columns.get(reqIndex).get(i): null;
			ITableEntry descEntry = descIndex >=0 ? columns.get(descIndex).get(i): null;
			String name = nameEntry != null ? nameEntry.getStringValue() : null;			
			String type = null;
			if(typeEntry != null){
				if(typeEntry instanceof HyperLinkEntry){
					type=((HyperLinkEntry)typeEntry).getObject().getCaption();
				}
				else{
					type=typeEntry.getStringValue() ;					
				}
			}
			String required = requiredEntry != null ? requiredEntry.getStringValue() : "fasle" ;
			String desc = descEntry != null ? descEntry.getStringValue() : null ;
			
			if(name==null){
				System.err.println("Null parameter name");
				continue;
			}
			if(type==null){
				System.err.println("Null parameter type");
				continue;
			}
			if(required==null){
				System.err.println("Null parameter required property");
				continue;
			}
			if(desc==null){
				System.err.println("Null parameter description");
				continue;
			}
			
			if(name==null)
				continue;
			
			name = name.toLowerCase().trim();
			
			type=type.trim().toLowerCase();			
			if(type.equals("date"))
				type="string";
			
			required=required.toLowerCase().trim();
			boolean bReq = required.toLowerCase().equals("yes") ? true : Boolean.parseBoolean(required);			
			
			Param param = new Param();
			param.setName(name);
			param.setType(type);
			param.setReqired(bReq);
			param.setDescription(desc);
			
			paramsMap.put(name, param);
		}
	}
}
